package mdp.solve;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.concurrent.ArrayBlockingQueue;

import dd.discrete.DD;

import util.Pair;
import util.Timer;
import mdp.id.ADDHandler;
import mdp.id.RDDL2DD;
import mdp.id.RDDL2DD.DEBUG;
import mdp.id.RDDL2DD.ORDER;

public class ModifiedPolicyIteration<T extends RDDL2DD> implements Runnable {
	private T _handler;
    private ArrayBlockingQueue<Pair<T, Integer>> _bq = null;
    private int evalLength = 0;
    private double epsilon;
    
	public ModifiedPolicyIteration(T _handler, ArrayBlockingQueue<Pair<T, Integer>> _bq,
			String domain, String instance,
			int evalLength, double epsilon) {
		super();
		this._handler = _handler;
		this._handler.buildFromFile(domain, instance);
		this._bq = _bq;
		this.evalLength = evalLength;
		this.epsilon = epsilon;
	}
    
	public void run() {
		int iter = 0;
		int v = _handler.DD_ZERO;
		boolean done = false;
		int pi = _handler.DD_ZERO;
		
		Timer solutionTimer = new Timer(); 
		Timer evalTimer = new Timer();
        Timer improvTimer =  new Timer();
        double evalerror = 0;
        
		while( !done ){
			
			solutionTimer.ResumeTimer();
			//policy imnprovement
			improvTimer.ResumeTimer();
			Pair<Integer, Integer> vpi = _handler.improvePolicy(v, pi, v);
			improvTimer.PauseTimer();
			
			if( pi == vpi._o2 ){
				System.err.println("plociy did not change there");
			}
			
			pi = vpi._o2;
//			if( pi != _handler.ConstrainThis(pi, null, false) ){
//				System.err.println("Improved policy violates constraint");
//				System.exit(1);
//			}
			
			//stopping criterion
			double error = _handler.diffSupNorm(vpi._o1, v);
			solutionTimer.PauseTimer();
			
			System.out.println("Policy #" + iter + " " + _handler.getSize(pi) + " " + 
					" " + _handler.countLeaves(pi) + " " + error + " " + _handler.getConstrainTime());
			
			if( Double.isNaN(error) ){
				System.err.println("BE = NaN");
				System.exit(1);
			}
			
			solutionTimer.ResumeTimer();
			if ( error < _handler.getStoppingValue(epsilon) ){
				done = true;
			}

			if( !done ){
				//policy eval
				
				evalTimer.ResumeTimer();
				
//				if( piC != _handler.ConstrainThis(piC, null, false) ){
//					System.err.println("Improved policy violates constraint");
//					System.exit(1);
//				}
				
				v = vpi._o1;
				
//				v = _handler.DD_ZERO;
				boolean evaldone = false;
				
				for( int i = 0 ; i < evalLength && !evaldone; ++i ){
					int vp = _handler.swapForPrimes(v);
					Pair<Integer, Integer> vq = _handler.regress(vp, pi, true, v, pi);
					evalerror = _handler.diffSupNorm(vq._o1, v);
					
					evalTimer.PauseTimer();
					solutionTimer.PauseTimer();
					System.out.println("Eval iter " + i + " " + evalerror + " " +
							_handler.getSize(vq._o1) + " " + _handler.getSize(vq._o2) + 
							" " + _handler.countLeaves(vq._o1, vq._o2));
					evalTimer.ResumeTimer();
					solutionTimer.ResumeTimer();
					
					if( evalerror < _handler.getStoppingValue(epsilon) ){
						evaldone = true;
					}
					v = vq._o1;
					_handler.flushCaches(false, pi, v);
				}
				evalTimer.PauseTimer();
				
			}
			
			++iter;
			
			_handler.flushCaches(false, v, pi);
			
			solutionTimer.PauseTimer();
			
			System.out.println("Iteration " + iter + " " + solutionTimer.GetElapsedTimeInMinutes() + " " + improvTimer.GetElapsedTimeInMinutes()
					+ " " + evalTimer.GetElapsedTimeInMinutes() + " " + _handler.getConstrainTime());
			
		}
	
		int piADD = _handler.makePolicyHelper(pi, new HashMap<Integer, Boolean>()); 
		 
		 ArrayList stats = new ArrayList();
        try{
        	stats.add(_handler.playDD(piADD));
        }catch(Exception e) {
        	e.printStackTrace();
        }
        
        stats.add(solutionTimer.GetElapsedTimeInMinutes());
        stats.add(_handler.getSize(v));
        stats.add(_handler.getSize(pi));
        
        stats.add(_handler.getConstrainTime());
        		
        System.out.println(stats);
        
		 if( _bq != null ){
             try {
//                     _bq.put(new Pair<T, Integer>(_handler, pi));
             	_bq.put(new Pair<T, Integer>(_handler, v));
             } catch (Exception e) {
                     e.printStackTrace();
             }
         }
		 
	}
    
	public static void main(String[] args) throws InterruptedException {
		ADDHandler handle = new ADDHandler(DEBUG.PROBLEM_INFO , ORDER.GUESS);
        Runnable mpi = 
                new ModifiedPolicyIteration<ADDHandler>(handle, null, 
                		args[0], args[1],
                		Integer.parseInt(args[2]), Double.parseDouble(args[3]));
        
        Thread mpiWorker = new Thread(mpi, "MPI");
        mpiWorker.start();
        mpiWorker.join();
	}
}
